
// %name WaveTemplate

// %define LSP_NEEDED
// %define ERROR_BODY =0
// %define LEX_BODY =0
// %define MEMBERS public: CloopWhileProgram *m_program; CloopWhileVariables m_vars; unsigned int m_depth;

%{
    #include <QString>
    #include <QList>
    #include "node.h"
    #include "template.h"
    #include "parser.h"
    #include "parameter.h"
    #include "parameterlist.h"
    #include "textnode.h"
    #include "body.h"
    #include "htmlelement.h"
    #include "htmlattribute.h"
    #include "htmlattributelist.h"
    #include "expressionlist.h"
    #include "expression.h"
    #include "htmlforeach.h"
    #include "htmlif.h"
    #include "operator.h"
    #include "constant.h"
    #include "templatecall.h"
    #include "var.h"
    #include "function.h"
    #include "component.h"
    #include "memberlist.h"
    #include "property.h"
    #include "membervar.h"
    #include "component.h"
    #include "memberevent.h"
    #include "membertemplate.h"
    #include "componentcall.h"
    #include "type.h"
    extern int parserlex (void);
    extern void parsererror( char* );

    #define YYERROR_VERBOSE
%}

%union {
    QString* str_buffer;
    int i;
    double f;
    Node* node;
}

/* Bison declarations.  */
%left '/'
%left '|'
%nonassoc '['
%nonassoc T_DOTDOT
%left T_AND T_OR
%nonassoc T_EQUAL T_NEQ T_LEQ T_GEQ '<' '>'
%left '-' '+'
%left '*' T_DIV
%left NEG     /* negation--unary minus */
%right '!'    /* not */
%right '^'    /* exponentiation */
%left '.'

%token T_VAR
%token T_TEMPLATE
%token <str_buffer> T_IDENTIFIER
%token <str_buffer> T_TEXT
%token T_FOREACH
%token T_IN
%token <str_buffer> T_STRING
%token T_EMBED_START
%token T_EMBED_END
%token <i> T_NUMBER
%token <f> T_FLOAT
%token T_FUNCTION
%token T_IF
%token <str_buffer> T_CALL
%token T_ERROR
%token T_TRUE
%token T_FALSE
%token T_ELSE
%token <str_buffer> T_SCRIPT
%token T_COMPONENT
%token T_PROPERTY
%token T_READONLY
%token T_EVENT
%token T_NULL
%token T_UPDATE
%token T_THEN

%type <node> members;
%type <node> members_opt;
%type <node> member;
%type <node> component;
%type <node> var;
%type <node> template;
%type <node> parameter;
%type <node> parameters;
%type <node> parameters_opt;
%type <node> body_opt;
%type <node> body;
%type <node> index_opt;
%type <node> attributes_opt;
%type <node> attributes;
%type <node> attribute;
%type <node> callparameters;
%type <node> callparameters_opt;
%type <str_buffer> metric_opt;
%type <node> nonif_exp;
%type <node> exp;
%type <node> path_exp;
%type <node> else_opt;
%type <node> function;
%type <str_buffer> script;
%type <str_buffer> script_opt;
%type <str_buffer> var_init_opt;
%type <node> property_init_opt;
%type <str_buffer> name_opt;
%type <node> component_call_body_opt;
%type <node> component_call_body;
%type <node> component_call_member;
%type <str_buffer> property_update;

%start main

%%

main: program
;

program: /* empty */
       | template program { Parser::self()->insertBefore( $1, Parser::self()->firstChild() ); }
       | function program { Parser::self()->insertBefore( $1, Parser::self()->firstChild() ); }
       | var program { Parser::self()->insertBefore( $1, Parser::self()->firstChild() ); }
       | component program { Parser::self()->insertBefore( $1, Parser::self()->firstChild() ); }
;

var: T_VAR '$' T_IDENTIFIER '=' exp ';' { $$ = new Var( *$3, $5, Type(Type::Scalar), @$ ); }
   | T_VAR '#' T_IDENTIFIER '=' exp ';' { $$ = new Var( *$3, $5, Type(Type::List), @$ ); }
;

function: T_FUNCTION '$' T_IDENTIFIER '(' parameters_opt ')' '{' script_opt '}' { $$ = new UserFunction( *$3, (ParameterList*)$5, *$8, Type(Type::Scalar), @$ ); }
        | T_FUNCTION '#' T_IDENTIFIER '(' parameters_opt ')' '{' script_opt '}' { $$ = new UserFunction( *$3, (ParameterList*)$5, *$8, Type(Type::List), @$ ); }
        | T_FUNCTION T_IDENTIFIER '(' parameters_opt ')' '{' script_opt '}' { $$ = new UserFunction( *$2, (ParameterList*)$4, *$7, Type(Type::Invalid), @$ ); }
;

template: T_TEMPLATE T_IDENTIFIER '(' parameters_opt ')' '{' body_opt '}' { $$ = new Template( *$2, (ParameterList*)($4), (Body*)($7), @$ ); }
;

component: T_COMPONENT T_IDENTIFIER '{' members_opt '}' { $$ = new Component( *$2, (MemberList*)($4), @$ ); }
;

parameters_opt: /* empty */ { $$ = new ParameterList( @$ ); }
              | parameters { $$ = $1; }
;

parameters: parameter { $$ = new ParameterList( @$ ); $$->appendChild( $1 ); }
                  | parameters ',' parameter { $$ = $1; $$->appendChild( $3 ); $$->includeLocation( @3 ); }
;

parameter: '$' T_IDENTIFIER { $$ = new Parameter( *$2, Type(Type::Scalar), @$ ); }
         | '#' T_IDENTIFIER { $$ = new Parameter( *$2, Type(Type::List), @$ ); }
         | T_IDENTIFIER { $$ = new Parameter( *$1, Type(Type::Invalid), @$ ); }
;

body_opt: /* empty */ { $$ = new Body( @$ ); }
                | body_opt body {
                    $$ = $1;
                    if ( dynamic_cast<TextNode*>($2) && dynamic_cast<TextNode*>($$->lastChild()) ) {
                        dynamic_cast<TextNode*>($$->lastChild())->appendText( dynamic_cast<TextNode*>($2)->text() );
                    }
                    else
                        $$->appendChild( $2 ); $$->includeLocation( @2 );
                }
;

body:   T_TEXT { $$ = new TextNode( *$1, @$ ); }
        | '<' T_IDENTIFIER name_opt attributes_opt '>' body_opt '<' '/' T_IDENTIFIER '>' {
                $$ = new HtmlElement( *$2, *$3, (HtmlAttributeList*)($4), (Body*)($6), @$ );
                if ( *$2 != *$9 ) { yyerror((char*)"Closing tag does not match opening tag"); YYERROR; }
               }
        | '<' T_IDENTIFIER name_opt attributes_opt '/' '>' {
                $$ = new HtmlElement( *$2, *$3, (HtmlAttributeList*)($4), new Body(@$), @$ );
               }
        | T_FOREACH '(' '$' T_IDENTIFIER index_opt T_IN exp ')' '{' body_opt '}' {
                $$ = new HtmlForeach( new Parameter(*$4, Type(Type::Scalar), @4), $5 ? (Parameter*)$5 : 0, $7, (Body*)$10, @$ );
				}
        | T_IF '(' exp ')' '{' body_opt '}' else_opt {
                $$ = new HtmlIf( $3, (Body*)$6, (HtmlIf*)$8,  @$ );
				}
        | T_EMBED_START exp T_EMBED_END { $$ = new Expression( $2, @$ ); }
		| T_CALL '(' callparameters_opt ')' {
                $$ = new TemplateCall( *$1, (ExpressionList*)$3, @$ );
				}
        | T_COMPONENT '(' T_IDENTIFIER name_opt attributes_opt ')' component_call_body_opt { $$ = new ComponentCall( *$3, *$4, (HtmlAttributeList*)$5, (MemberList*)$7, @$); }
;

component_call_body_opt: /* empty */ { $$ = new MemberList(@$); }
                  | '{' '}' { $$ = new MemberList(@$); }
                  | '{' component_call_body '}' { $$ = $2; }
;

component_call_body: component_call_member { $$ = new MemberList(@1); $$->appendChild( $1 ); }
              | component_call_body component_call_member { $$ = $1; $$->appendChild( $2 ); $$->includeLocation(@2); }
;

component_call_member: template { $$ = $1; }
                | function { $$ = $1; }
;

name_opt: /* empty */ { $$ = new QString(); }
        | '$' T_IDENTIFIER { $$ = new QString( *$2 ); }
;

else_opt: /* empty */ { $$ = 0; }
        | T_ELSE '{' body_opt '}' { $$ = new HtmlIf( 0, (Body*)$3, 0, @$ ); }
        | T_ELSE T_IF '(' exp ')' '{' body_opt '}' else_opt {
            $$ = new HtmlIf( $4, (Body*)$7, (HtmlIf*)$9, @$ );
          }
;

index_opt: /* empty */ { $$ = 0; }
         | ',' '$' T_IDENTIFIER { $$ = new Parameter( *$3, Type(Type::Scalar), @3 ); }
;

attributes_opt: /* empty */ { $$ = new HtmlAttributeList(@$); }
              | attributes { $$ = $1; }
;

attributes: attribute { $$ = new HtmlAttributeList(@$); $$->appendChild( $1 ); }
                  | attributes attribute { $$ = $1; $$->appendChild( $2 ); $$->includeLocation( @2 ); }
;

attribute: T_IDENTIFIER '=' T_STRING { $$ = new HtmlAttribute( *$1, *$3, @$ ); }
         | T_IDENTIFIER '=' T_EMBED_START exp T_EMBED_END { $$ = new HtmlAttribute( *$1, $4, @$ ); }
;

callparameters_opt: /* empty */ { $$ = new ExpressionList(@$); }
				 | callparameters { $$ = $1; }
;
 
callparameters: exp { $$ = new ExpressionList(@$); $$->appendChild( new Expression( $1, @1 ) ); }
          | callparameters ',' exp { $$ = $1; $$->appendChild( new Expression( $3, @3 ) ); $$->includeLocation( @3 ); }
;

metric_opt: /* empty */ { $$ = 0; }
	| T_IDENTIFIER { $$ = $1; }
;

exp: nonif_exp { $$ = $1; }
   | T_IF nonif_exp T_THEN exp T_ELSE exp { $$ = new IfOperator( $2, $4, $6, @$ ); }
;

nonif_exp: T_NUMBER metric_opt { $$ = new Constant( $1, $2 ? *$2 : QString::null, @$ ); }
   | T_FLOAT metric_opt { $$ = new Constant( $1, $2 ? *$2 : QString::null, @$ ); }
   | T_STRING { $$ = new Constant( *$1, Constant::String, @$ ); }
   | T_IDENTIFIER { $$ = new Constant( *$1, Constant::VariableIdentifier, @$ ); }
   | T_TRUE { $$ = new Constant( Constant::True, @$ ); }
   | T_FALSE { $$ = new Constant( Constant::False, @$ ); }
   | T_NULL { $$ = new Constant( Constant::Null, @$ ); }
   | '$' '$' { $$ = new Constant( Constant::LambdaParameter, @$ ); }
   | nonif_exp '.' T_IDENTIFIER { $$ = new DotOperator($1, *$3, @2); }
   | nonif_exp '.' T_IDENTIFIER '(' callparameters_opt ')' { $$ = new DotCallOperator($1, *$3, (ExpressionList*)$5, @2); }
   | nonif_exp '+' nonif_exp { $$ = new BinaryOperator( $1, $3, Operator::Add, @2 ); }
   | nonif_exp '-' nonif_exp { $$ = new BinaryOperator( $1, $3, Operator::Sub, @2 ); }
   | nonif_exp '*' nonif_exp { $$ = new BinaryOperator( $1, $3, Operator::Mul, @2 ); }
   | nonif_exp '/' path_exp { $$ = new PathOperator( $1, $3, @2 ); }
   | nonif_exp '|' nonif_exp { $$ = new BinaryOperator( $1, $3, Operator::Filter, @2 ); }
   | nonif_exp '<' nonif_exp { $$ = new BinaryOperator( $1, $3, Operator::Less, @2 ); }
   | nonif_exp '>' nonif_exp { $$ = new BinaryOperator( $1, $3, Operator::Greater, @2 ); }
   | nonif_exp T_EQUAL nonif_exp { $$ = new BinaryOperator( $1, $3, Operator::Equal, @2 ); }
   | nonif_exp T_NEQ nonif_exp { $$ = new BinaryOperator( $1, $3, Operator::NotEqual, @2 ); }
   | nonif_exp T_GEQ nonif_exp { $$ = new BinaryOperator( $1, $3, Operator::GreaterOrEqual, @2 ); }
   | nonif_exp T_LEQ nonif_exp { $$ = new BinaryOperator( $1, $3, Operator::LessOrEqual, @2 ); }
   | nonif_exp T_AND nonif_exp { $$ = new BinaryOperator( $1, $3, Operator::And, @2 ); }
   | nonif_exp T_OR nonif_exp { $$ = new BinaryOperator( $1, $3, Operator::Or, @2 ); }
   | nonif_exp T_DIV nonif_exp { $$ = new BinaryOperator( $1, $3, Operator::Div, @2 ); }
   | '-' nonif_exp  %prec NEG { $$ = new UnaryOperator( $2, Operator::Neg, @1 ); }
   | '!' nonif_exp { $$ = new UnaryOperator( $2, Operator::Not, @1 ); }
   | nonif_exp '^' nonif_exp  { $$ = new BinaryOperator( $1, $3, Operator::Power, @2 ); }
   | '(' exp ')' metric_opt { if ( !$4 ) $$ = $2; else $$ = new MetricOperator( $2, *$4, @4 ); }
   | nonif_exp '[' exp ']' { $$ = new BinaryOperator( $1, $3, Operator::Index, @$ ); }
   | nonif_exp '[' T_DOTDOT exp ']' { $$ = new BinaryOperator( $1, new RangeOperator( 0, $4, @3 ), Operator::Index, @$ ); }
   | nonif_exp '[' exp T_DOTDOT ']' { $$ = new BinaryOperator( $1, new RangeOperator( $3, 0, @4 ), Operator::Index, @$ ); }
   | nonif_exp '[' exp T_DOTDOT exp ']' { $$ = new BinaryOperator( $1, new RangeOperator( $3, $5, @4 ), Operator::Index, @$ ); }
   | T_IDENTIFIER '(' callparameters_opt ')' { $$ = new CallOperator( *$1, (ExpressionList*)$3, @$ ); }
;

path_exp: T_IDENTIFIER { $$ = new Constant( *$1, Constant::TagIdentifier, @$ ); }
        | '#' T_IDENTIFIER { $$ = new Constant( *$2, Constant::TagListIdentifier, @$ ); }
        | T_IDENTIFIER '(' callparameters_opt ')' { $$ = new CallOperator( *$1, (ExpressionList*)$3, @$ ); }
        | path_exp '|' nonif_exp { $$ = new BinaryOperator( $1, $3, Operator::Filter, @2 ); }
        | path_exp '[' exp ']' { $$ = new BinaryOperator( $1, $3, Operator::Index, @$ ); }
        | path_exp '[' T_DOTDOT exp ']' { $$ = new BinaryOperator( $1, new RangeOperator( 0, $4, @3 ), Operator::Index, @$ ); }
        | path_exp '[' exp T_DOTDOT ']' { $$ = new BinaryOperator( $1, new RangeOperator( $3, 0, @4 ), Operator::Index, @$ ); }
        | path_exp '[' exp T_DOTDOT exp ']' { $$ = new BinaryOperator( $1, new RangeOperator( $3, $5, @4 ), Operator::Index, @$ ); }
;

script_opt: /* empty */ { $$ = new QString(""); }
          | script { $$ = $1; }
;

script: T_SCRIPT { $$ = $1; }
      | script T_SCRIPT { $1->append(*$2); $$ = $1; }
;

members_opt: /* empty */ { $$ = new MemberList( @$ ); }
              | members { $$ = $1; }
;

members: member { $$ = new MemberList( @$ ); $$->appendChild( $1 ); }
                  | members member { $$ = $1; $$->appendChild( $2 ); $$->includeLocation( @2 ); }
;

member: T_FUNCTION T_IDENTIFIER '(' parameters_opt ')' '{' script_opt '}' { $$ = new MemberFunction( *$2, (ParameterList*)$4, *$7, Type(Type::Invalid), @$ );}
      | T_FUNCTION '~' T_IDENTIFIER '(' parameters_opt ')' '{' script_opt '}' { $$ = new MemberFunction( "~" + *$3, (ParameterList*)$5, *$8, Type(Type::Invalid), @$ );}
      | T_FUNCTION '$' T_IDENTIFIER '(' parameters_opt ')' '{' script_opt '}' { $$ = new MemberFunction( *$3, (ParameterList*)$5, *$8, Type(Type::Scalar), @$ );}
      | T_FUNCTION '#' T_IDENTIFIER '(' parameters_opt ')' '{' script_opt '}' { $$ = new MemberFunction( "#" + *$3, (ParameterList*)$5, *$8, Type(Type::List), @$ );}
      | T_VAR '$' T_IDENTIFIER var_init_opt ';' { $$ = new MemberVar( *$3, *$4, Type(Type::Scalar), @$ ); }
      | T_VAR '#' T_IDENTIFIER var_init_opt ';' { $$ = new MemberVar( *$3, *$4, Type(Type::List), @$ ); }
      | T_PROPERTY '$' T_IDENTIFIER property_update { $$ = new Property( *$3, Type(Type::Scalar), 0, false, *$4, @$ ); }
      | T_PROPERTY '#' T_IDENTIFIER property_update { $$ = new Property( *$3, Type(Type::List), 0, false, *$4, @$ ); }
      | T_PROPERTY T_READONLY '$' T_IDENTIFIER property_init_opt ';' { $$ = new Property( *$4, Type(Type::Scalar), $5, true, QString::null, @$ ); }
      | T_PROPERTY T_READONLY '#' T_IDENTIFIER property_init_opt ';' { $$ = new Property( *$4, Type(Type::List), $5, true, QString::null, @$ ); }
      | T_EVENT T_IDENTIFIER ';' { $$ = new MemberEvent( *$2, @$ ); }
      | T_TEMPLATE T_IDENTIFIER '(' parameters_opt ')' ';' { $$ = new MemberTemplate( *$2, (ParameterList*)$4, @$ ); }
      | template { $$ = $1; }
;

property_update: ';' { $$ = new QString( QString::null );; }
               | '{' T_UPDATE '{' script_opt '}' '}' { $$ = $4; }
;

property_init_opt: /* empty */ { $$ = 0; }
            | '=' exp { $$ = $2; }

var_init_opt: /* empty */ { $$ = new QString( QString::null ); }
            | '=' script { $$ = $2; }
;
%%

